/*
>> PixelToPixelOp.s
>>
>> Neighbourhood operation skeleton for Xetal, allowing a single buffer input,
>> scalar inputs, and scalar + buffer outputs. Adapted from PixelToPixelOp.s.
>>
>> Concessions made to to the limitations of Xetal:
>> * No dynamic indirect addressing
>> * Static lmems instead of buffers
>> * No addressing of X neighbourhoods larger than -2..2
>>
>> Author: Wouter Caarls
>> Date:   01-08-2005
*/

#include "stdsc.h"
#include "stdstratego.h"
#include "xtcstrategies.h"

stratego code expandarrayindex(code locals, code body)
{
  imports
    analyse-locality

  strategies
    main =
      ?(larr, body);
      !"symbols"; table-create;
      !larr; \ArrayInit(a) -> a\; map(\AssignInit(CodeConst(a)) -> a\);
      map(add-local); !body; alltd(ExpandArrayIndex)

  rules
    ExpandArrayIndex:
      ArrayIndex(d, i) -> XTCArrayIndex(d, i, <Print>(j))
      where (<is-local>(d); <addi>(<bottomup(try(Eval))>(i), 1) => j)

    Eval:
      IntConst(a) -> <string-to-int>(a)

    Eval:
      Negative(a) -> <subti>(0, a)

    Print:
      a -> IntConst(<int-to-string>(a))
      where (<pos>(a))

    Print:
      a -> Negative(IntConst(<abs; int-to-string>(a)))
      where (<neg>(a))
}

typedef int reg;
typedef void lmem;

void NeighbourhoodToPixelOp(argcode_t *argcode, int arguments, code body)
{
int _fi;
extents_t _extents[1];
reg _ii, _stride, _first = 0;
code locals[];

lmem larg0, larg1, larg2, larg3, larg4;
reg  rarg0, rarg1, rarg2, rarg3, rarg4;

_extents[0].end = _extents[0].size = 0;

/* Build a list of locals, and replace argument occurances */
for (_ii=0; _ii < arguments; _ii++)
{
  if (argcode[_ii].argtype == ARG_STREAM_IN)
  {
    @declare(`void*`, argcode[_ii].id);

    _extents[0].end =  MAX(_extents[0].end , argcode[_ii].extents[0].end );
    _extents[0].size = MAX(_extents[0].size, argcode[_ii].extents[0].size);    
    locals[@len(locals)] = makeid(`larg`, _ii);
  }
  else if (argcode[_ii].argtype == ARG_STREAM_OUT)
  {
    /* Replace occurances */
    body = replace(deref(argcode[_ii].id), makeid(`larg`, _ii), body); 
    locals[@len(locals)] = makeid(`larg`, _ii);
  }
  else
  {
    /* *** ONLY WORKS FOR IMMEDIATES *** */
    @declare(addpointer(argcode[_ii].datatype), argcode[_ii].id);
    @argcode[_ii].id = &(@argcode[_ii].argid).data.d;
  }
  argcode[_ii].state.buffer = 1; /* Buffer is internal */
  argcode[_ii].state.state = 0;
}

/* Before replacing on input, reduce to do loop unrolling */
body = reduce(body);

for (_ii=0; _ii < arguments; _ii++)
{
  if (argcode[_ii].argtype == ARG_STREAM_IN)
  {
    int _jj;

    _fi = _ii;

    for (_jj=0; _jj < _extents[0].size; _jj++)
    {
      if (argcode[_ii].extents[0].size-_jj-1 == 0)
      {
	/* Replace by direct argument */
	body = replace(replace(`id`, argcode[_ii].id,
				replace(`i`, _jj + argcode[_ii].extents[0].start,
					`id[i]`)),
			makeid(`larg`, _ii), body);
      }
      else if (argcode[_ii].extents[0].size-_jj-1 >= 0)
      {
	/* Replace by appropriately delayed argument */
	@declarestatic(`lmem`, makeid(`_in`, argcode[_ii].extents[0].size-_jj-1));

	/* Replace occurances */
	body = replace(replace(`id`, argcode[_ii].id,
				replace(`i`, _jj + argcode[_ii].extents[0].start,
					`id[i]`)),
			makeid(`_in`, argcode[_ii].extents[0].size-_jj-1+
			       (_extents[0].size-argcode[_ii].extents[0].size)), body);
	
	locals[@len(locals)] = makeid(`_in`, argcode[_ii].extents[0].size-_jj-1+
				      (_extents[0].size-argcode[_ii].extents[0].size));
      }
    }
    argcode[_ii].state.delay = 0;
  }
  else if (argcode[_ii].argtype == ARG_STREAM_OUT)
  {
    argcode[_ii].state.delay = _extents[0].end;
  }
}
	
/* Propagate locals */
locals = @getlocals(locals, body);

/* If-conversion */
body = ifconversion(locals, body); 

/* Propagate locals */
locals = @getlocals(locals, body);

/* Replace all types by the "reg" type, and
 * Make all locals lmems instead of regs */
body = makelmem(locals, makereg(body));

/* Convert references to lmem single array indices to XTC
 * list-array indices */
body = expandarrayindex(locals, body);

linebyline(argcode, arguments)
{
}
{
  @body;

  /* Shift data */
  for (_ii=argcode[_fi].extents[0].size-1; _ii > 1;_ii--)
    @makeid(`_in`, _ii) = @makeid(`_in`, _ii-1);
  
 /* Load data */
  if (_extents[0].size > 1)
    _in1 = @makeid(`larg`, _fi);	  
}
{
};
}
